package com.wh.common.annotation.aspect;

import com.wh.common.annotation.MyAsync;
import com.wh.common.util.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Executor;

/**
 * @author luweije
 */

@Aspect
@Component
@Slf4j
public class MyAsyncAspect {

    @Resource
    private Executor myAsyncThreadPool;

    @Pointcut("@annotation(com.wh.common.annotation.MyAsync)")
    public void pointCut() {}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        MyAsync myAsync = method.getAnnotation(MyAsync.class);
        if (myAsync == null) {
            executeBusiness(point);
            return null;
        }
        Executor executor = myAsyncThreadPool;
        String selfPoolName = myAsync.value();
        if (StringUtils.isNotBlank(selfPoolName)) {
            // 获取spring容器的实例
            Executor selfPool = getBeanByName(selfPoolName);
            executor = selfPool == null ? executor : selfPool;
        }
        Map<String, String> contextMap = MDC.getCopyOfContextMap();
        executor.execute(() -> {
            MDC.setContextMap(contextMap);
            executeBusiness(point);
        });
        return null;
    }

    private Executor getBeanByName(String selfPoolName) {
        try {
            Object bean = SpringContextUtil.getBean(selfPoolName);
            if (bean instanceof Executor) {
                return (Executor) bean;
            }
        } catch (Exception e) {
            log.error("获取自定义线程异常 name={}", selfPoolName, e);
            return null;
        }
        log.error("自定义的线程池类型目前不支持 name={}", selfPoolName);
        return null;
    }

    private void executeBusiness(ProceedingJoinPoint point) {
        try {
            point.proceed();
        } catch (Throwable e) {
            log.error("异步执行业务代码异常", e);
        }
    }


}
